﻿#define _CRT_SECURE_NO_WARNINGS 1

//！的优先级非常高

#include<string.h>
/*
求字符串长度
strlen
长度不受限制的字符串函数
strcpy
strcat
strcmp
长度受限制的字符串函数介绍
strncpy
strncat
strncmp
字符串查找
strstr
strtok
错误信息报告
strerror
字符操作
内存操作函数
memcpy
memmove
memset
memcmp
*/

//1.1 strlen
//size_t strlen ( const char * str );
//字符串已经 '\0' 作为结束标志，strlen函数返回的是在字符串中 '\0' 前面出现的字符个数（不包含 '\0' )。
//参数指向的字符串必须要以 '\0' 结束。
//注意函数的返回值为size_t，是无符号的（ 易错 ）  size_t==unsigned int
//学会strlen函数的模拟实现

//#include <string.h>



//int main()
//{
//	//char arr[] = "abcdef";//abcdef\0
//	char arr[] = { 'b', 'i', 't' };
//	//[][][][][][b][i][t][][][][][][][]
//	int len = strlen(arr);//随机值  从b开始数
//
//	printf("%d\n", len);
//
//	return 0;
//}
//



//int main()
//{
//	//        3                  6
//	//if (strlen("abc") - strlen("abcdef") > 0)
//	if (strlen("abc") > strlen("abcdef"))     也可以强制类形转化为int
//	{
//		printf(">\n");
//	}
//	else
//	{
//		printf("<=\n");
//	}
//	return 0;
//}

/*
所以，表达式中各种长度可能小于int长度的整型值，都必须先转
换为int或unsigned int，然后才能送入CPU去执行运算。
int +- int 得到int类型
size_t +-  size_t 得到size_t类型
*/


/*

assert(<expression>);  
  当expression结果为“假”时，会在stderr中输出这条语句所在的文件名和行号，以及这条表达式。
  这只在调试版本中起作用，在Release版本中不会产生任何代码。  
  通常当我们使用assert时，都在强烈说明一个含义：在这里必然如此。
  它通常用于一个函数的先验条件和后验条件的检查。
  比如我写一个C风格复制字符串的函数，并且认为调用者不应该传入NULL指针：(NULL指针是空指针，可以理解为0，即假)  
*/
#include<assert.h>
//size_t my_strlen(const char *p)    //size_t==unsigned int 
//{
//	//const 让*p不能被修改
//	assert(p != NULL);     //指针不能为空指针
//	size_t count = 0;
//	while (*p != 0)
//	{
//		count++;
//		p++;
//	}
//	return count;
//}


//2.指针-指针
////3.递归
//size_t my_strlen(const char* p)
//{
//
//}
//
//
//
//int main()
//{
//	char arr[50] = "abcdedf";
//	size_t ret = my_strlen(arr);
//	printf("%u\n", ret);
//	return 0;
//}



//1.2 strcpy
/*
char* strcpy(char * destination, const char * source );

Copies the C string pointed by source into the array pointed by destination, including the
terminating null character (and stopping at that point).

Return Value

Each of these functions returns the destination string. No return value is reserved to indicate an error.



源字符串必须以 '\0' 结束。   (\0意味着字符串的结束)
会将源字符串中的 '\0' 拷贝到目标空间。
目标空间必须足够大，以确保能存放源字符串。
目标空间必须可变。将拷贝的内容，替换源字符串最头上的内容

*/
#include<stdio.h>

//strcpy 1.拷贝 2.替换
//
//int main()
//{
//	char name[20] = "xxxxxxxxxxxxxxxxx";
//	char arr[] = "zhan\0gsan";
//	strcpy(name, "zhan\0gsan");     //也可以替换成arr    ，\0也拷贝
//	printf("%s\n", name);      //%s只打印到\0之前
//	return 0;
//}

//char* strcpy(char* destination, const char* source);
//int main()
//{
//	char arr[] = "abcdef";
//	 char* p = "momo";
//	strcpy(arr, p);   //与字符串相关的，参数是两个指针
//	return 0;
//}
//
//int main()
//{
//	char* p = "abcdef";   //常量字符串，没法改
//	char arr[] = "momo";
//	strcpy(p, arr);   //与字符串相关的，参数是两个指针
//	//不对！ p里面是常量，不能修改
//	return 0;
//}

//
//void my_strcpy(char* p2, const char* p1)
//{
//	assert(p1 != NULL);
//	assert(p2 != NULL);   //指针需要解引用 ，要保证不是空指针，一定要初始化
//	while (*p1 != 0)
//	{
//		*(p2 ) = *(p1 );     //p2不能是空指针，空指针不能解引用
//		//覆盖，不是交换
//		
//		p1++;       //p++就会让指针往后走
//		p2++;
//
//		//p2如果不完全初始化，会用0自动补充
//		//但是如果不初始化，就是空指针！
//	}
// *p2=0;  //把\0也拷贝进去
//}
//
////空指针：不指向任何数据的指针
//
//int main()
//{
//	char arr1[] = "abcdef";
//	char arr2[20] = { 0 };
//	my_strcpy(arr2, arr1);
//	printf("%s\n", arr2);
//}


//字符串追加 strcat   （因为是追加，所以不能结束）

/*
 strcat   
 char * strcat ( char * destination, const char * source );
 Appends a copy of the source string to the destination string. The terminating null character
in destination is overwritten by the first character of source, and a null-character is included
at the end of the new string formed by the concatenation of both in destination.

and a null-character is included at the end of the new string formed by the concatenation of both in destination.(补充\0)

！源！字符串必须以 '\0' 结束。
目标空间必须有足够的大，能容纳下源字符串的内容。
目标字符串在被连接的时候，会去掉\0，所以源字符串，要有\0
目标空间必须可修改。

库函数的const char 类型的形参可以接收char类型的实参(将实参当作常量来对待)

Return Value

Each of these functions returns the destination string (strDestination). 
No return value is reserved to indicate an error


*/

//==判断 =赋值   抽象表达式，随便带个式子试一试

//字符串函数 ，参数一般都是地址，都可以放地址

//strcat不能自己对自己追加，但是strncat可以

#include<assert.h>
////
//// p1需要修改 源字符串传过来当作不能被修改的字符串使用，所以const修饰，而不是const char接收
//char* my_strcat(char* p1,const char* p2)   //传过来之后，就不可修改
//{
//	assert(p1);
//	assert(p2);
//	int count = 0;
//	while (*p1 != 0)
//	{
//		p1++;      //p1也得向后走
//		//没必要计算有多长，只需要让他走到最后就可以了
//		count++;
//	}
//	while (*p2 != 0)    //源字符串必须\0结尾
//	{
//		*p1 = *p2;
//		p1++;
//		count++;
//		p2++;
//	}
//	return  (p1 - count);
//}
//
////不完全初始化，后续都会初始化为0
// 
// 
// 
//int main()
//{
//	char arr1[20] = "hello ";
//    char arr2[6] = "world";
//	my_strcat(arr1, arr2);   //不能自己对自己追加
//	printf("%s\n", arr1);   //也没有返回值，可能是崩溃了：死循环。。。。。。
//	//arr1也可以替换为my_stract   但是替换后需要消除重复的my_strcat语句
//	return 0;
//}








/*
* 1.4 strcmp
int strcmp ( const char * str1, const char * str2 );
 
This function starts comparing the first character of each string. If they are equal to each
other, it continues with the following pairs until the characters differ or until a terminating
null-character is reached.
标准规定：
第一个字符串大于第二个字符串，则返回大于0的数字
第一个字符串等于第二个字符串，则返回0
第一个字符串小于第二个字符串，则返回小于0的数字

Value Relationship of string1 to string2
< 0 string1 less than string2
0 string1 identical to string2
> 0 string1 greater than string2


字符串大小的比较，不是以字符串的长度直接决定，
而是从最左边第一个字符开始比较，大者为大，小者为小，
若相等，则继续按字符串顺序比较后面的字符


两个不同长度的字符串进行比较时，不是长的字符串就一定大。如字符串s1为ABCE，字符串s2为ABCDEF。对 s1 与 s2 进行比较时，s1 的第4个字符是E，s2 的第4个字符是D，而D < E，所以s1 > s2。尽管 s2 比s1长。

当字符串有空格时，空格也参加比较。如s1为 b ook(表示空格)，s2 为book，显示-79，故s1 < s2

大写字母和小写字母的ASCII代码值是有区别的，所以，yes > YEs

当字符串全部用英文字母的大写(或小写)组成时，字符串的大小顺序和它们在字典中的顺序相同

由汉字组成的字符串可以参加比较。如李红 < 王军。它们的大小实际是由其拼音构成的字符串的大小来决定的。上例即：LIHONG < WANGJUN

*/

/*
1.区别：break 结束整个循环过程，不再判断执行循环的条件是否成立；
		continue 只结束本次循环，即跳出循环体中下面尚未执行的语句， 接着进行下一次是否执行循环的判定。
*/
//
//int my_strcmp(const char* p1, const char* p2)
//{
//	assert(p1 && p2);
//	while (*p1 != 0 && *p2 != 0)
//	{
//		if (*p1 > *p2)       //p1和p2是指针，应该是字符去比较
//		{
//			return 1;
//		}
//		else if (*p1 < *p2)
//		{
//			return - 1;
//		}
//		else
//		{
//			if (*p1 == *p2)
//			{
//				p1++;
//				p2++;
//				continue;
//			}
//		}
//		p1++;
//		p2++;
//	}
//	if (*p1 == 0 && *p2 == 0)
//	{
//		return 0;
//	}
//	else if (*p1 == 0)
//	{
//		return -1;
//	}
//	else
//	{
//		return 1;
//	}
//}

//
//
//
//int my_strcmp(const char* str1, const char* str2)
//{
//	assert(str1 && str2);
//	while (*str1 == *str2)  //一对一对比，比较当作判断条件
//	{
//		if (*str1 == '\0')
//			return 0;//相等
//		str1++;
//		str2++;
//	}
//	return (*str1 - *str2);
//	//返回正负值，可以直接相减。
//}
//
//
//
//int main()
//{
//	//	//比较一下两个字符串是否相等:一个字节一个字节地比
////	//arr1是数组名，数组名是数组首元素的地址
////	//arr2是数组名，数组名是数组首元素的地址
////	/*if (arr1 == arr2)
////	{
////		printf("==\n");
////	}
////	else
////	{
////		printf("!=\n");
////	}*/
////
//
//	//int strcmp(const char* str1, const char* str2);   ..字符串函数参数全是首元素的地址
//
//
//	char arr1[20] = "zhangsan";
//	char arr2[20] = "lhangsanf";
//	int ret = my_strcmp(arr1, arr2);   //数组名就是首元素的地址
//	if (ret > 0)
//	{
//		printf(">\n");
//	}
//	else if (ret == 0)
//	{
//		printf("==\n");
//
//	}
//	else
//	{
//		printf("<\n");
//
//	}
//	return 0;
//}

/*
长度不限制的字符串函数是指可以处理任意长度字符串的函数。
这些函数可以接受任意长度的字符串作为输入，并且不会受到字符串长度的限制。
它们可以用于处理大型文本文件、长字符串或其他需要处理大量字符的情况。
eg
strlen strcmp strcat strcpy
*/



//长度受限制的字符串函数：你给我限定个数，你让我干多少，我就干多少
//strncpy
//strncat
//strncmp



/*
1.5 strncpy
char * strncpy ( char * destination, const char * source, size_t num );     //num：需要拷贝的字节数

Copies the first num characters of source to destination. If the end of the source C string
(which is signaled by a null-character) is found before num characters have been copied,
destination is padded with zeros until a total of num characters have been written to it.
拷贝num个字符从源字符串到目标空间。
如果源字符串的长度小于num，则拷贝完源字符串之后，在目标的后边追加0，直到num个。

Return Value

Each of these functions returns strDest. No return value is reserved to indicate an error.



*/
//
//
//int main()
//{
//	char arr1[20] = "abcdef";
//	char arr2[] = "hello bit";
//	char arr3[] = "he";
//	//strcpy(arr1,arr2);
//	
//	//只想拷贝hello
//	//strncpy(arr1, arr2, 5);
//	strncpy(arr1, arr3, 5);   //不足num个时，用\0补充
//	printf("%s\n", arr1);
//	return 0;
//}





/*
1.6 strncat
char * strncat ( char * destination, const char * source, size_t num );   //追加num个

Appends the first num characters of source to destination, plus（加） a terminating（终止） null-character.  //结束之后，额外添加以一个\0
If the length of the C string in source is less than num, only the content up to the terminating
null-character is copied   //只复制中止字符\0之前的内容  

//只复制中止字符\0之前的内容
//所以说源字符串，必须是\0结尾

Return Value

Each of these functions returns a pointer to the destination string(指向dest字符串的指针). No return value is reserved to indicate an error.


strncat 一定能且只能追加一个\0


*/
//
//int main()
//{
//	char arr1[20] = "hello\0xxxxxxxxxxx";
//	char arr2[] = "world";
//	strncat(arr1, arr2, 3);     
//	//通过arr加一个\0去调试发现，追加完成之后会自动补充一个\0
//
//	//在追加完之后，自动补充\0
//
//
//	printf("%s\n", arr1);
//	return 0;
//}








/*
1.7 strncmp
int strncmp ( const char * str1, const char * str2, size_t num );

//Compares up to(可以到达num) num characters of the C string str1 to those of the C string str2.
比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完

Return Value Description
< 0 string1 substring less than string2 substring
0 string1 substring identical to string2 substring
> 0 string1 substring greater than string2 substring



*/

//
//
//int main()
//{
//	char arr1[] = "abcdef";
//	char arr2[] = "abc";
//	int ret = strncmp(arr1, arr2, 5);    
//	//比较到出现另个字符不一样或者!!一个字符串结束!!或者num个字符全部比较完
//
//	//在VS中，>0 <0 =0  VS的返回值统一为1 -1 0.
//	//其余的不一定。C标准不是这么规定的
//
//	if (ret == 0)     //if句判断条件，写==  ==是判断 =是赋值
//	{
//		printf("==");
//	}
//	else
//	{
//		printf("!=");
//	}
//	return 0;
//}




/*
1.8 strstr：查找子串       （strstr查找自己的子串strstr）
char * strstr ( const char *str1, const char * str2);

Returns a pointer to the first occurrence of str2(子) in !!!str1!! ,
or a null pointer if str2 is not part of str1

*/

//两个字符串之间的对比，画两个箭头，分别往后走
//当需要重新来过的时候，可以在某个重新来过之前的节点p，创建一个指针，记住这个地址。然后再回到p++往下走。
//同时采用额外建立指针的方式，放置str1和str2乱动，有时需要多个指针

//在逻辑比较复杂的一对字符串中，往往需要多个指针,来记住某个特定的地址




	//让str1和str2不动，保证能找到起始位置。
	// 只有指针解引用，才会让实参发生改变



//char* my_strstr(const char* str1, const char* str2)
//{
//	assert(str1 && str2);
//	const char* s1 = str1;
//	const char* s2 = str2;
//	const char* p = str1;
// 
// //s1从p的地址往下遍历
//
//	while (*p)
//	{
//		s1 = p;   //每一次s1都从p开始
//		s2 = str2;
// 
// 当不能确定数组长短的时候，就用\0来控制循环 判断长短
// 
//		while (*s1!='\0' && *s2!='\0' && *s1 == *s2)   //相等往后走  //\0没必要比
//		{
//			s1++;
//			s2++;
//		}
// 
//   //如果出现s1！=s2，那么就不会出现s2==0.只有当s1一直等于s2，s2遍历完，才会==0
// 
//		if (*s2 == '\0')        //*s1 == *s2，在这个条件下，只有s2遍历完，才能==0
//		{
//			return (char*)p;      //p在str1中遍历      //return 不能返回const的数据。
// //没法修改实参，但是可以返回与实参相关的值
//		}
//		p++;     //while停下来p++，p往后走
//	} 
//	return NULL;    p遍历完，都不行，返回NULL
//}

//	
//
//int main()
//{
//	char email[] = "zpw@bitejiuyeke.com";
//	char sub[] = "bitejiuyeke";
//	char* ret = my_strstr(email, sub);   //(返回类型是指针)
//	printf("%s\n",ret);  //in str1 
//	return 0;
//}






/*
 strtok  :切割字符串

 char * strtok ( char * str, const char * sep );     //可以传指针，可以传字符串

 sep参数是个字符串，定义了用作分隔符的字符集合
第一个参数指定一个字符串，它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标
记。

strtok函数找到str中的下一个标记，并将其用 \0 结尾，   返回一个指向这个标记的指针。
（注：
strtok函数会改变被操作的字符串，所以在使用strtok函数切分的字符串一般都是临时拷贝的内容
并且可修改。）
strtok函数的第一个参数不为 NULL ，函数将找到str中第一个标记，strtok函数将保存它在字符串
中的位置。
strtok函数的第一个参数为 NULL ，函数将在同一个字符串中被保存的位置开始，查找下一个标
记。

//也就是说，在想用这个传，可以传NULL，继续使用。
//重新传参别的str1时，str1重新传，重新分割。
strtok函数的第一个参数不为 NULL ，函数将找到str中第一个标记，strtok函数将保存它在字符串
中的位置。


如果字符串中不存在更多的字符，则返回 NULL 指针
//找到\0，没东西找了，返回NULL


static 出了函数，变量的值不销毁，再次进去，还是这个值，不会重建（记忆功能）

*/



//strtok 会修改字符串为\0
//会将标记改为\0，并返回这个字段的起始位置
//
//int main()
//{
//	char sep[] = ".@";
//	char email[50] = "zpw@bitejiuyeke.com";   //只与str1的出现顺序有关
//
//	//char email[50] = "zpw@bite@jiuyeke.com";   //只与str1的出现顺序有关、标记点可以重复使用
//	//这个时候会打印zpw bite jiuyeke   sep中“@.”可以重复使用
//
//	//char email[50] = "zpw@.@bitejiuyeke.com";  //如果是这种情况，还是会打印三个串，连续的分隔符，当作一个分割符处理
//
//
//	//char email[50] = "zpw@\0bitejiuyeke.com";   //只与str1的出现顺序有关
//	//如果是这种情况，会打印 zpw （null）  （null）
//	//原因：将第一个@变为0之后，下一次会从@后面的\0进行读取，则为第三种情况，没东西读取，返回null
//
//
//
//	//替换的时候，与sep字符串顺序无关，与str1中标记点的顺序有关。只要包含sep标记点，按照str1出现的顺序截断
//
//	char ce[30] = { 0 };
//	strcpy(ce, email);
//	//char*p1=strtok(ce, ".@");    //@替换成0  //返回一个初位置指针
//	//char*p2=strtok(NULL, sep);  //.换成0
//
//
//	//char*p3=strtok(NULL, sep);  //没有替换的，则把最后一段截取。找到末尾\0也会停下来
//	////当后面没有标记点，但还有内容，不是0的时候，再次传参str1为NULL，则会直接读取后面的内容到0位置，返回首元素的指针
//
//	//printf("%s\n%s\n%s", p1, p2,p3);  //打印到0
//	char* ret = NULL;
//	for (ret = strtok(ce, sep); 
//		ret != NULL;                   //这个串不管有多少字段，都全部拆分
//		ret = strtok(NULL, sep))     //初始化只执行一次  ret迟早会变成NULL（返回值）  控制语句：每次都执行
//	{
//		printf("%s\n", ret);
//	}
//		return 0;
//}







#include<errno.h>

/*
1.10 strerror

char * strerror ( int errnum );

返回错误码，所对应的错误信息。

*/


//C语言的库函数，在执行失败的时候，都会设置错误码
//0 1 2 3 4 5 6 7 8 
//

//
//int main()
//{
//	/*printf("%s\n", strerror(0));
//	printf("%s\n", strerror(1));
//	printf("%s\n", strerror(2));
//	printf("%s\n", strerror(3));
//	printf("%s\n", strerror(4));
//	printf("%s\n", strerror(5));*/
//
//
//	//errno---C语言设置的一个存放错误码的全局变量：始终记录的是最新的错误码
//
//	FILE*pf = fopen("text.txt", "r");    //用读的方式打开一个文件   
//	//打开成功，返回一个有效的地址，执行失败，返回一个空指针
//	if (pf == NULL)
//	{
//		printf("%s\n", strerror(errno));   
//		//没有这样的文件或目录  No such file or directory
//		return 1;
//	}
//	else
//	{
//		printf("%s\n", strerror(errno));
//
//	}
//	return 0;
//}




/*
字符分类函数：   参数是int类型，但是都可以写‘字符’
函数 如果他的参数符合下列条件就返回真 
iscntrl 任何控制字符
isspace 空白字符：空格‘ ’，换页‘\f’，换行'\n'，回车‘\r’，制表符'\t'或者垂直制表符'\v'
isdigit 十进制字符数字 ‘0’~‘9’
isxdigit 十六进制数字，包括所有十进制数字，小写字母a~f，大写字母A~F
islower 小写字母a~z
isupper 大写字母A~Z
isalpha 字母a~z或A~Z
isalnum 字母或者数字，a~z,A~Z,0~9
ispunct 标点符号，任何不属于数字或者字母的图形字符（可打印）
isgraph 任何图形字符    :图像
isprint 任何可打印字符，包括图形字符和空白字符

*/

#include<ctype.h>
#include<errno.h>


//
//int main()
//{
//	int ret = isupper('C');   //字符必须单引号
//	printf("%d\n", ret);
//	printf("%s\n", strerror(errno));
//	ret = isspace('\f');
//	printf("%d\n", ret);
//	printf("%s\n", strerror(errno));
//	ret = isspace('w');
//	printf("%d\n", ret);
//	printf("%s\n", strerror(errno));   //函数成功执行，只不过‘w’不是 white-space character
//	ret = isdigit('5');
//	printf("%d\n", ret);
//	printf("%s\n", strerror(errno));
//
//
//	return 0;
//}



/*
字符转化函数:返回转化后的字符
toupper

int toupper ( int c );

return :
The uppercase equivalent to c, if such value exists, or c (unchanged) otherwise.
The value is returned as an int value that can be implicitly casted to char.

大写字母等效于c（如果存在此值），否则等效于c。
该值以int值的形式返回，该值可以隐式转换为char。


tolower

The lowercase equivalent to c, if such value exists, or c (unchanged) otherwise.
The value is returned as an int value that can be implicitly casted to char.

*/

#include<stdlib.h>
int main()
{
	printf("%c\n", toupper('\n'));  //否则返回原值（打印\n）
	printf("%c\n", toupper('2'));    //打印字符2
	printf("%c\n", toupper('@'));    //打印字符2
	printf("%c\n", toupper('W'));
	printf("%c\n", toupper('w'));
	printf("%c\n", tolower('w'));
	printf("%c\n", tolower('W'));
	return 0;
}